#!/usr/bin/env perl

# Copyright (c) 2011, Ryan M. Lefever
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
#       copyright notice, this list of conditions and the following
#       disclaimer in the documentation and/or other materials provided
#       with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# DESCRIPTION: This program replaces text in one or more files.

# enable perl's safety info
use strict;
use warnings;
use File::Basename;

# Global variables
my @inputFiles;
my @fromStrs;
my @toStrs;
my $verbose = 0;

main();
exit(0);

# Runs a command. The output is not placed on stdout and stderr.
sub runcmd{
  my ($cmd) = @_;
  if($verbose){ print "CMD: " . $cmd . "\n"; }
  `$cmd`;
}

# Parse command line arguments.
sub parsecla{
  if($#ARGV == 0 && ($ARGV[0] eq "--help" || $ARGV[0] eq "-h")){
    usage();
    exit(0);
  }

  my $foundFiles = 0;
  my $getToStr = 0;
  my $i = 0;
  for($i=0; $i <= $#ARGV; ++$i){
    if(!$foundFiles){
      if($ARGV[$i] eq "--"){ $foundFiles = 1; }
      else{
	if($getToStr){ push @toStrs, $ARGV[$i]; $getToStr = 0; }
	else{ push @fromStrs, $ARGV[$i]; $getToStr = 1; }
      }
    }
    else{ push @inputFiles, $ARGV[$i]; }
  }

  if($#inputFiles < 0){ print "No input files listed.\n"; usage(); exit(1); }
  if($#fromStrs < 0){ print "No replacements specified.\n"; usage(); exit(1); }
  if($#fromStrs != $#toStrs){
    print "The number of from-strings does not match the number of " .
      "to-strings.\n";
    usage();
    exit(1);
  }
}

sub main{
  parsecla();

  my $i;
  for($i=0; $i <= $#inputFiles; ++$i){
    my ($base, $dir, $suffix) = fileparse($inputFiles[$i]);
    my $tmpFile = $dir . "." . $base . $suffix . "_" . time() . "_" . $$;
    while(-e $tmpFile){ $tmpFile = $tmpFile . int(rand(10)); }
    # In order to get the same access control, copy the original file to
    # the tmp file first
    runcmd("cp -a " . $inputFiles[$i] . " " . $tmpFile);
    if(open(RFH, $inputFiles[$i])){
      if($verbose){ print "Opened " . $inputFiles[$i] . " for reading\n"; }
      if(open(WFH, "> " . $tmpFile)){
	if($verbose){ print "Opened " . $inputFiles[$i] . " for writing\n"; }

	# Peform replacement in tmp file
	my $line;
	my $fileChanged = 0;
	while($line = <RFH>){
	  my $j;
	  for($j=0; $j <= $#fromStrs; ++$j){
	    $_ = $line;
	    if(s/$fromStrs[$j]/$toStrs[$j]/g){ $fileChanged = 1; }
	    $line = $_;
	  }
	  print WFH $line;
	}
	close WFH;
	close RFH;
	if($fileChanged){ print "Changed " . $inputFiles[$i] . "\n"; }

	# Remove original file
	runcmd("rm -f " . $inputFiles[$i]);

	# Mv tmp file to original
	runcmd("mv -f " . $tmpFile . " " . $inputFiles[$i]);
      }
      else{
	close RFH;
	print "Unable to open temporary file " . $tmpFile . "\n";
      }
    }
    else{ print "Unable to open " . $inputFiles[$i] . "\n"; }
  }
}

# Prints usage information
sub usage{
  print "replace --help |\n";
  print "replace -h |\n";
  print "replace from to [from to] ... -- file [file] ...\n";
}
